package easylog

import (
	"bufio"
	"context"
	"fmt"
	"gitee.com/hongzhaomin/hzm-common-go/strutil/color"
	"github.com/mattn/go-isatty"
	"io"
	"log/slog"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
	"unicode"
)

// EasyHandler 自定义日志后端 slog.EasyHandler
var _ slog.Handler = (*EasyHandler)(nil)

type EasyHandler struct {
	level         slog.Leveler      // 设置日志级别
	filePath      string            // 日志文件地址
	file          *os.File          // 日志文件
	fileIsTerm    bool              // file 是否是终端
	remote        io.Writer         // 远程输出对象
	latestLogTime *time.Time        // 最新的日志输出时间，用于判断是否分割日志文件
	mu            *sync.Mutex       // 锁
	jsonHandler   *slog.JSONHandler // json日志格式输出 handler，用于输出到远程
}

// NewEasyHandler 创建新的日志 handler
func NewEasyHandler(filePath string, remote io.Writer, level slog.Leveler) *EasyHandler {
	handler := &EasyHandler{
		level:    level,
		filePath: filePath,
		remote:   remote,
		mu:       &sync.Mutex{},
	}

	if filePath != "" {
		// 日志文件只能写，文件不存在则创建，文件存在则追加写入
		dir, _ := filepath.Split(filePath)
		if dir != "" {
			err := os.MkdirAll(dir, 0666)
			if err != nil {
				fmt.Printf("open log file error:%v\n", err)
			}
		}
		file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			fmt.Printf("open log file error:%v\n", err)
		}
		handler.file = file
		handler.fileIsTerm = handler.isTerm(file)
	}

	handler.jsonHandler = slog.NewJSONHandler(remote, &slog.HandlerOptions{
		AddSource: true,
		Level:     level,
	})

	return handler
}

// Enabled 当前日志级别是否开启
func (h *EasyHandler) Enabled(ctx context.Context, level slog.Level) bool {
	return h.jsonHandler.Enabled(ctx, level)
}

// Handle 处理日志记录，仅在 Enabled() 返回 true 时才会被调用
func (h *EasyHandler) Handle(ctx context.Context, record slog.Record) error {
	r := Record{
		Record: record,
	}
	// 自身handler处理日志
	err2 := h.myHandle(ctx, r)

	// remote 日志
	if h.remote != nil {
		_ = h.jsonHandler.Handle(ctx, record)
	}

	if err2 != nil {
		return err2
	}
	return err2
}

func (h *EasyHandler) myHandle(_ context.Context, r Record) error {
	source := r.Source()
	shortFile := h.getShortFile(source.File, source.Line)

	formatMsgFun := func(levelColor, functionColor, resetColor color.Color) string {
		return fmt.Sprintf("%v - %s%-5s%s --- [%s%30s%s] %s: %s\n",
			r.Time.Format("2006-01-02 15:04:05.000"),
			levelColor, r.Level, resetColor,
			functionColor, source.Function, resetColor,
			shortFile, r.Message)
	}

	// 无颜色日志
	logMsg := formatMsgFun(color.Empty, color.Empty, color.Empty)

	// 有颜色日志
	logMsgColor := formatMsgFun(logLevelColorMap[r.Level], color.CyanText, color.Reset)

	h.mu.Lock()
	defer h.mu.Unlock()
	_, err2 := os.Stdout.Write([]byte(logMsgColor))

	//if h.fileIsTerm {
	//	h.logWriteFile([]byte(logMsgColor), r)
	//} else {
	//	h.logWriteFile([]byte(logMsg), r)
	//}
	h.logWriteFile([]byte(logMsg), r)
	return err2
}

func (h *EasyHandler) logWriteFile(logMsg []byte, r Record) {
	if h.file == nil {
		return
	}

	thisLogTime := r.Time
	latestLogTime := h.latestLogTime
	if latestLogTime == nil {
		// latestLogTime 为空，极有可能是程序重启了，日志文件还在
		scanner := bufio.NewScanner(h.file) // 创建一个新的Scanner来读取文件
		seekLatestLogTime := ""
	next:
		for scanner.Scan() { // 每次调用Scan都会读取下一行，直到EOF或错误发生
			line := scanner.Text() // 获取当前行的文本
			if len(line) < 10 {
				continue
			}

			date := line[0:10]
			for _, ch := range strings.ReplaceAll(date, "-", "") {
				if !unicode.IsNumber(ch) {
					continue next
				}
			}
			seekLatestLogTime = date
			break
		}

		// 检查是否有错误发生，比如读取过程中的IO错误
		if err := scanner.Err(); err != nil {
			if err != io.EOF {
				fmt.Printf("read log file error:%v\n", err)
			}
			goto startOut
		}

		if seekLatestLogTime != "" {
			t, err2 := time.Parse(time.DateOnly, seekLatestLogTime)
			if err2 != nil {
				fmt.Printf("parse log file time error:%v\n", err2)
				goto startOut
			}
			latestLogTime = &t
		}
	}

startOut:
	// 替换当前日志时间
	h.latestLogTime = &thisLogTime

	// 判断是否需要分割日志文件
	isCutLogFile := latestLogTime != nil &&
		(thisLogTime.Month() != latestLogTime.Month() || thisLogTime.Day() != latestLogTime.Day())
	if isCutLogFile {
		// 开始分割日志文件
		err2 := h.file.Close()
		if err2 != nil {
			fmt.Printf("close log file error:%v\n", err2)
		}

		oldPath := h.filePath
		fileName := filepath.Base(oldPath)            // xxx.log
		ext := filepath.Ext(fileName)                 // .log
		name := strings.ReplaceAll(fileName, ext, "") // xxx
		dir := strings.ReplaceAll(oldPath, fileName, "")
		newPath := fmt.Sprintf("%s%s-%s%s", dir, name, latestLogTime.Format("20060102"), ext)

		// 旧文件进行重命名存档
		if err2 = os.Rename(oldPath, newPath); err2 != nil {
			fmt.Printf("rename log file error:%v\n", err2)
		}

		// 重新创建一个新的日志文件
		file, err3 := os.OpenFile(h.filePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err3 != nil {
			fmt.Printf("open log file error:%v\n", err3)
		}
		h.file = file
	}

	_, err2 := h.file.Write(logMsg)
	if err2 != nil {
		fmt.Printf("log msg write to file error:%v\n", err2)
	}
}

func (h *EasyHandler) println(x string, r Record) {
	h.mu.Lock()
	defer h.mu.Unlock()
	x = fmt.Sprintf("%s\n", x)
	_, err2 := os.Stdout.Write([]byte(x))

	h.logWriteFile([]byte(x), r)
	if err2 != nil {
		fmt.Printf("println log file error:%v\n", err2)
	}
}

// WithAttrs 从现有的 handler 创建一个新的 handler，并将新增属性附加到新的 handler
func (h *EasyHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
	clone := h.clone()
	clone.jsonHandler = h.jsonHandler.WithAttrs(attrs).(*slog.JSONHandler)
	return clone
}

// WithGroup 从现有的 handler 创建一个新的 handler，并将指定分组附加到新的 handler
func (h *EasyHandler) WithGroup(name string) slog.Handler {
	clone := h.clone()
	clone.jsonHandler = h.jsonHandler.WithGroup(name).(*slog.JSONHandler)
	return clone
}

func (h *EasyHandler) isTerm(out io.Writer) bool {
	if w, ok := out.(*os.File); !ok ||
		(!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) {
		return false
	}
	return true
}

func (h *EasyHandler) clone() *EasyHandler {
	// We can't use assignment because we can't copy the mutex.
	return &EasyHandler{
		level:         h.level,
		filePath:      h.filePath,
		file:          h.file,
		fileIsTerm:    h.fileIsTerm,
		remote:        h.remote,
		latestLogTime: h.latestLogTime,
		mu:            h.mu, // mutex shared among all clones of this handler
	}
}

func (h *EasyHandler) getShortFile(file string, line int) string {
	short := file
	for i := len(file) - 1; i > 0; i-- {
		if file[i] == '/' {
			short = file[i+1:]
			break
		}
	}
	return short + ":" + strconv.Itoa(line)
}
